using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
using System.ComponentModel;

namespace Pavilion
{
    public class ContainerNode : NodeBase
    {
        public event Action<NodeBase> NodeAdded, NodeRemoved;

        public EntryNode EntryNode { get; private set; }
        public NotifiableCollection<NodeBase> NodeCollection { get { return nodeDictionary.ValueCollection; } }

        private NotifiableDictionary<string, NodeBase> nodeDictionary { get; set; }

        public ContainerNode(string name) : base()
        {
            nodeDictionary = new NotifiableDictionary<string, NodeBase>();
            EntryNode = new EntryNode();

            AddNode(EntryNode);

            this.Name = name;
        }

        public override void Dispose()
        {
            RemoveAllNodes();
            EntryNode = null;
        }

        public void AddNode(NodeBase node)
        {
            node.ParentContainer = this;
            nodeDictionary.Add(node.Name, node);
            node.NameChanged += new NameChangedDelegate(Node_NameChanged);

            OnNodeAdded(node);
        }

        public void RemoveNode(string nodeName)
        {
            if (nodeName == EntryNode.Name)
                return;

            NodeBase nodeToRemove = this[nodeName];
            EntryNode.RemoveLinksByNode(nodeToRemove);

            foreach (NodeBase node in nodeDictionary.ValueCollection)
                node.RemoveLinksByNode(nodeToRemove);

            nodeToRemove.NameChanged -= new NameChangedDelegate(Node_NameChanged);
            nodeDictionary.Remove(nodeName);
            nodeToRemove.Dispose();

            OnNodeRemoved(nodeToRemove);
        }

        private void RemoveAllNodes()
        {
            foreach (NodeBase node in nodeDictionary.ValueCollection)
                node.Dispose();

            nodeDictionary.Clear();
        }

        public void LinkNode(string current, string next, IEnumerable<ILock> lockList, OpenCriteria openCriteria)
        {
            this.LinkNode(this[current], this[next], lockList, openCriteria);
        }

        public void LinkNode(NodeBase current, NodeBase next, IEnumerable<ILock> lockList, OpenCriteria openCriteria)
        {
            current.AddLink(next, lockList, openCriteria);
        }

        public bool Contains(string nodeName)
        {
            foreach (NodeBase node in nodeDictionary.ValueCollection)
            {
                if (node.Name == nodeName)
                    return true;
            }
            return false;
        }

        /// <summary>
        /// Validates the container and all its inner Nodes. Returns true if no error is found.
        /// </summary>
        public bool Validate()
        {
            if (nodeDictionary.Count == 0)
                return false;

            foreach (NodeBase node in nodeDictionary.ValueCollection)
            {
                if (node.GetType() == typeof(ContainerNode))
                {
                    if (!((ContainerNode)node).Validate())
                        return false;
                }
            }
            return true;
        }

        public string GetUniqueName(string prefix)
        {
            int count = 0;
            string animationName = prefix + count++;

            while (nodeDictionary.ContainsKey(animationName))
                animationName = prefix + count++;

            return animationName;
        }

        public override string ToString()
        {
            return Name;
        }

        public NodeBase this[string nodeName]
        {
            get { return nodeDictionary[nodeName]; }
        }

        private void OnNodeAdded(NodeBase node)
        {
            if (NodeAdded != null)
                NodeAdded(node);
        }

        private void OnNodeRemoved(NodeBase node)
        {
            if (NodeRemoved != null)
                NodeRemoved(node);
        }

        private void Node_NameChanged(string oldName, string newName)
        {
            nodeDictionary.ReplaceKey(oldName, newName);
        }
    }
}